//! # Author
//! * 沈戟峰 Jerry Shen <jerry_shen_sjf@qq.com>
//! Updated from open source jsp project CookieShop.
//! Original open source project does not have a author signature.
//! 本应用程序来源于开源软件蛋糕商城，原作者并未签名。
//! 增强后蛋糕商城使用Axum, tokio, sqlx, mariadb, html, javascript, jquery,json
//! 原有的JSP界面和管理后台已移植至上述技术栈，仍可以正常使用
//! 原有的Servlet API已移植至Axum，改变不大
//! Controller层以下的API基本都使用Rust通用代码生成器莲花，红莲尝鲜版三十一生成。
//! 部分原有的API也已经移植完毕。
//! 除了原有的前台和后台，现在的蛋糕商城有一个Rust通用代码生成器莲花生成的集成后台。
#![allow(non_snake_case, unused_imports)]
use axum::{http::StatusCode, routing::get_service, Router};

use tower_sessions::{MemoryStore, SessionManagerLayer as SessionLayer};

use tracing_subscriber::layer::SubscriberExt as _;
use tracing_subscriber::util::SubscriberInitExt as _;
use tracing_subscriber::EnvFilter;
use std::net::SocketAddr;
use tower_http::services::ServeDir;
use CookieShopRust::AppState;
use CookieShopRust::config::Config;
use tower_http::cors::{Any, CorsLayer};
use http::{Method};
use CookieShopRust::controller::goods_controller::goods_controller;
use CookieShopRust::controller::cust_order_controller::cust_order_controller;
use CookieShopRust::controller::pay_type_controller::pay_type_controller;
use CookieShopRust::controller::status_controller::status_controller;
use CookieShopRust::controller::order_item_controller::order_item_controller;
use CookieShopRust::controller::goods_type_controller::goods_type_controller;
use CookieShopRust::controller::recommend_controller::recommend_controller;
use CookieShopRust::controller::role_controller::role_controller;
use CookieShopRust::controller::permission_controller::permission_controller;
use CookieShopRust::controller::user_controller::user_controller;
use CookieShopRust::custcontroller::admin_goods_cust_controller::admin_goods_cust_controller;
use CookieShopRust::custcontroller::admin_cust_order_cust_controller::admin_cust_order_cust_controller;
use CookieShopRust::custcontroller::admin_user_cust_controller::admin_user_cust_controller;
use CookieShopRust::custcontroller::admin_goods_type_cust_controller::admin_goods_type_cust_controller;
use CookieShopRust::custcontroller::index_cust_controller::index_cust_controller;
use CookieShopRust::custcontroller::goods_cust_controller::goods_cust_controller;
use CookieShopRust::custcontroller::goods_type_cust_controller::goods_type_cust_controller;
use CookieShopRust::custcontroller::cust_order_cust_controller::cust_order_cust_controller;
use CookieShopRust::custcontroller::order_item_cust_controller::order_item_cust_controller;
use axum::middleware::from_fn;
use CookieShopRust::middleware::login_middleware::check_auth;
use CookieShopRust::controller::login_controller::login_controller;
use CookieShopRust::controller::profile_controller::profile_controller;

#[tokio::main]
async fn main() {
    let store = MemoryStore::default();
    let session_layer = SessionLayer::new(store);
	let cors = CorsLayer::new()
	// allow `GET` and `POST` when accessing the resource
	.allow_methods([Method::GET, Method::POST])
	// allow requests from any origin
	.allow_origin(Any)
	.allow_headers(Any);

	// Set the RUST_LOG, if it hasn't been explicitly defined
	if std::env::var_os("RUST_LOG").is_none() {
		std::env::set_var(
			"RUST_LOG",
			"example_static_file_server=debug,tower_http=debug",
		)
	}
	let routes = Router::new()
		.nest("/goodsController", goods_controller())
		.nest("/custOrderController", cust_order_controller())
		.nest("/payTypeController", pay_type_controller())
		.nest("/statusController", status_controller())
		.nest("/orderItemController", order_item_controller())
		.nest("/goodsTypeController", goods_type_controller())
		.nest("/recommendController", recommend_controller())
		.nest("/roleController", role_controller())
		.nest("/permissionController", permission_controller())
		.nest("/userController", user_controller())
		.nest("/profileController", profile_controller())
		.layer(from_fn(check_auth));

	let custRoutes = Router::new()
		.nest("/adminGoodsCustController", admin_goods_cust_controller())
		.nest("/adminCustOrderCustController", admin_cust_order_cust_controller())
		.nest("/adminUserCustController", admin_user_cust_controller())
		.nest("/adminGoodsTypeCustController", admin_goods_type_cust_controller())
		.nest("/indexCustController", index_cust_controller())
		.nest("/goodsCustController", goods_cust_controller())
		.nest("/custOrderCustController", cust_order_cust_controller())
		.nest("/goodsTypeCustController", goods_type_cust_controller())
		.nest("/orderItemCustController", order_item_cust_controller());

	let app = Router::new()
		.nest_service(
			"/pages",
			get_service(ServeDir::new("./template/pages/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/custpages",
			get_service(ServeDir::new("./template/custpages/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
			.nest_service(
				"/css",
			get_service(ServeDir::new("./template/css/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/js",
			get_service(ServeDir::new("./template/js/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/layer",
			get_service(ServeDir::new("./template/layer/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/fonts",
			get_service(ServeDir::new("./template/fonts/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/easyui",
			get_service(ServeDir::new("./template/easyui/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
				.nest_service(
			"/echarts",
			get_service(ServeDir::new("./template/echarts/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/images",
			get_service(ServeDir::new("./template/images/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/error",
			get_service(ServeDir::new("./template/error/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/uploadjs",
			get_service(ServeDir::new("./template/uploadjs/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest_service(
			"/login",
			get_service(ServeDir::new("./template/login/")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.route(
			"/",
			get_service(ServeDir::new("./template/index.html")).handle_error(|error: std::convert::Infallible| async move {
				(
					StatusCode::INTERNAL_SERVER_ERROR,
					format!("Unhandled internal error: {}", error),
				)
			}),
		)
		.nest("/controller/", routes)
		.nest("/custcontroller/", custRoutes)
		.nest("/loginController",login_controller())
		.layer(cors)
		.layer(session_layer);

	let addr = SocketAddr::from(([127, 0, 0, 1], 8082));
	tracing::debug!("listening on {}", addr);
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}
